//============================================================================
// Author      : Ting Lei, leiting@gmail.com
// Version     :
// Copyright   : all rights reserved, see LICENSE file
// Description : entry for postgres functions
//============================================================================

#include <postgres.h>
#include <funcapi.h>
#include <executor/spi.h>

#include <string.h>
#include "input_t.h" //for input table
#include "output_t.h"   //for output

#include "my_func.h" //transfer C to C-plus

extern char *dup_pgtext(text *what); //from input.c


PG_FUNCTION_INFO_V1(mincost_ns_c);

Datum mincost_ns_c(PG_FUNCTION_ARGS)
{
    FuncCallContext     *funcctx;
    int                  call_cntr;
    int                  max_calls;
    TupleDesc            tupdesc;
    AttInMetadata       *attinmeta;
    char* array_str;
    int string_len;

    int count;  //number of input table
    double result_distance;
    text* network_sql;
    char* nw_sql_c;
    text* src;
    text* dst;
    char* src_c;
    char* dst_c;
    int64 requiredFlow;
    static output_t* mincost_ns_output_tuples;
    input_t* edge_tuples = NULL;

    /* stuff done only on the first call of the function */
    if (SRF_IS_FIRSTCALL())
    {
        MemoryContext   oldcontext;

        /* create a function context for cross-call persistence */
        funcctx = SRF_FIRSTCALL_INIT();

        /* switch to memory context appropriate for multiple function calls */
        oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);

//        elog(NOTICE,"mincost_ns_c(): entering");  //debug

    	network_sql = PG_GETARG_TEXT_PP(0); //lt
    	src = PG_GETARG_TEXT_PP(1);//lt
    	dst = PG_GETARG_TEXT_PP(2);//lt
    	requiredFlow = PG_GETARG_INT64(3); //lt

        /* total number of tuples to be returned */
    	nw_sql_c = dup_pgtext(network_sql);
        count = readtable_mincost(nw_sql_c, &edge_tuples);
        SPI_pfree(nw_sql_c);


        /* C-plus */
        src_c = dup_pgtext(src);
        dst_c = dup_pgtext(dst);
        for (int ii = 0; ii < count; ii++) {
        	input_t t = edge_tuples[ii];
        }
        int result_length;
        int result_code = mincost_ns_process(edge_tuples, count, src_c, dst_c, requiredFlow, &mincost_ns_output_tuples, &result_length);
        elog(NOTICE, "mincost_ns_c(): mincost_process returned  %d (input %d)", result_length, count);  //debug

        for (int ii = 0; ii < result_length; ii++) {
        	output_t t = mincost_ns_output_tuples[ii];
        }

        funcctx->max_calls = result_length;
        // funcctx->max_calls = count;

        /* Free the input structures  */
        /*
        if (edge_tuples != NULL){
            for (int i=0; i<count; i++){
                if (edge_tuples[i].origin_c != NULL){
                    SPI_pfree(edge_tuples[i].origin_c);
                }
                if (edge_tuples[i].destination_c != NULL){
                    SPI_pfree(edge_tuples[i].destination_c);
                }
                if (edge_tuples[i].vname_c != NULL){
                    SPI_pfree(edge_tuples[i].vname_c);
                }
            }
            SPI_pfree(edge_tuples);
        }
        //*/

        // funcctx->max_calls = count;
        if (funcctx->max_calls == 0){
            SRF_RETURN_DONE(funcctx);
        }

        /* Build a tuple descriptor for our result type */
        if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("function returning record called in context "
                            "that cannot accept type record")));

        /*
         * generate attribute metadata needed later to produce tuples from raw
         * C strings
         */
        attinmeta = TupleDescGetAttInMetadata(tupdesc);
        funcctx->attinmeta = attinmeta;

        MemoryContextSwitchTo(oldcontext);

    }

    /* stuff done on every call of the function */
    funcctx = SRF_PERCALL_SETUP();

    call_cntr = funcctx->call_cntr;
    max_calls = funcctx->max_calls;
    attinmeta = funcctx->attinmeta;
    // result_tuples = (body_t *)funcctx->user_fctx;



    if (call_cntr < max_calls)    /* do when there is more left to send */
    {
        char       **values;
        HeapTuple    tuple;
        Datum        result;

        /*
         * Prepare a values array for building the returned tuple.
         * This should be an array of C strings which will
         * be processed later by the type input functions.
         */
        values = (char **) palloc(5 * sizeof(char *));
        //string values
        values[0] = (char *) dup_cstring(mincost_ns_output_tuples[call_cntr].origin_c);
        values[1] = (char *) dup_cstring(mincost_ns_output_tuples[call_cntr].destination_c);
        //numeric values
        values[2] = (char *) palloc(20 * sizeof(char));
        values[3] = (char *) palloc(20 * sizeof(char));
        values[4] = (char *) palloc(20 * sizeof(char));
        values[5] = (char *) palloc(20 * sizeof(char));
        values[6] = (char *) dup_cstring(mincost_ns_output_tuples[call_cntr].vname_c);

        snprintf(values[2], 20, "%ld", mincost_ns_output_tuples[call_cntr].capacity_c);
        snprintf(values[3], 20, "%ld", mincost_ns_output_tuples[call_cntr].lowerbound_c);
        snprintf(values[4], 20, "%lf", mincost_ns_output_tuples[call_cntr].cost_c);
        snprintf(values[5], 20, "%ld", mincost_ns_output_tuples[call_cntr].value_c);


        /* build a tuple */
        tuple = BuildTupleFromCStrings(attinmeta, values);

        /* make the tuple into a datum */
        result = HeapTupleGetDatum(tuple);
        /* clean up (this is not really necessary) */
        pfree(values[0]);
        pfree(values[1]);
        pfree(values[2]);
        pfree(values[3]);
        pfree(values[4]);
        pfree(values[5]);
        pfree(values[6]);
        pfree(values);

        SRF_RETURN_NEXT(funcctx, result);
    }
    else    /* do when there is no more left */
    {
    	// pfree(funcctx->user_fctx);
        if (mincost_ns_output_tuples != NULL)
            pfree(mincost_ns_output_tuples); //only for test
        SRF_RETURN_DONE(funcctx);
    }
//    elog(NOTICE, "mincost_ns_c(): ok4"); //debug
}

// Call by Cplus functions
output_t* get_space_mincost(int size){
	output_t* output_tuples = (output_t*)palloc(size * sizeof(output_t));
    return output_tuples;
}


void set_tuple_mincost(int i, const char * origin_c, const char * destination_c, int64_t capacity_c, int64_t lowerbound_c, edget_cost_t cost_c, int64_t value_c, const char * vname_c, output_t** p_output_tuples){
	output_t *output_tuples = * p_output_tuples;
	output_tuples[i].origin_c = dup_cstring(origin_c);//pfree??
	output_tuples[i].destination_c = dup_cstring(destination_c);//pfree??
	output_tuples[i].capacity_c = capacity_c;
	output_tuples[i].lowerbound_c = lowerbound_c;
	output_tuples[i].cost_c = cost_c;
	output_tuples[i].value_c = value_c;
	output_tuples[i].vname_c = dup_cstring(vname_c);
}
